# -*- coding: utf-8 -*-
import pygame
import blockengine
from tupleVector import *
import os
from anim import *
import copy

DEBUG = False

class cAnimationSet (object):
    """
    Animation set contains a bundle of animations - one for every direction 
    """
    def __init__ (self, name, folder):
        self.name = name
        self.folder = folder
        self.dAnim = {}
        self.load (name, folder)
    def __repr__(self):
        return "<AnimationSet: "+self.name+" with "+str(len(self.dAnim))+" animations>"
    def load(self, name, folder):
        r = ["0","45","90","135","180","225","270","315"]
        direction = ["South", "SouthWest", "West", "NorthWest", "North", "NorthEast", "East", "SouthEast"]
        path = os.path.join (folder, name)
        rc = 0
        for d in direction: 
            f = os.path.join (path, r[rc] + ".tar.gz" )
            anim = AnimNew (f)
            self.dAnim [d] = anim
            rc += 1
    

class cState(object):
    """ 
    State Manager
    """
    def __init__ (self, name, callback, lAnimSets, folderName):
        self.name = name
        self.dAnimSet = {}
        self.callback = callback
        self.folderName = folderName
        for l in lAnimSets:
            set = self.loadAnimationSet (l)
            self.registerAnimationSet(l, set)
        self.activeSet = None
    def __repr__ (self):
        return "<State: "+self.name+" with "+str(len(self.dAnimSet))+" Animation sets>"
    def registerAnimationSet (self, name, set):
        self.dAnimSet[name] = set 
    def loadAnimationSet(self, setName):
        return cAnimationSet (setName, self.folderName)
    def getActiveSet (self): return self.activeSet
    def setActiveSet (self, setName): self.activeSet = self.dAnimSet[setName]
    def isActiveSet (self, setName): 
        if self.activeSet == self.dAnimSet[setName]: 
            return True
        return False
    def nextFrame (self, direction, timer):
        " get next frame from active animation set for girven 'direction'"
        if self.activeSet == None: 
            print "cState: Error: nextFrame but no active Anim Set"
            return None 
        return self.activeSet.dAnim[direction].nextFrame(timer)
        

class cHuman(blockengine.cSprite):
    """ 
    Sprite Class to handle human-like charakters and animations (skeletons and men)
    Implements State Manager, pathfinding and animation
    folderName = subfolder name in "char" folder inn Filesystem 
    """
    def __init__(self, folderName, tPosPi=(0,0), img=None, depth=0, zBuffer=None):
        blockengine.cSprite.__init__(self, tPosPi, img, depth, zBuffer)
        self.dStates = {}
        self.state = None 
        self.speed = 1
        self.folderName = os.path.join ("res", "char", folderName) 
        self.setColCircle (100) # FIXME 
    def setSpeed (self, speed): self.speed = speed
    def getSpeed (self): return self.speed
    def registerState (self, statename, callback, lAnimSets):
        self.dStates[statename] = cState (statename, callback, lAnimSets, self.folderName)
    
    def getDrawSurface (self):
        "get frame from state and animation"
        if self.state == None: return
        if self.heading >= 0 and self.heading < 22.5:
            frame = self.state.nextFrame ("South", pygame.time.get_ticks())
        if self.heading >= 22.5 and self.heading < 67.5:
            frame = self.state.nextFrame ("SouthEast", pygame.time.get_ticks())
        if self.heading >= 67.5 and self.heading < 112.5:
            frame = self.state.nextFrame ("East", pygame.time.get_ticks())
        if self.heading >= 112.5 and self.heading < 157.5:
            frame = self.state.nextFrame ("NorthEast", pygame.time.get_ticks())
        if self.heading >= 157.5 and self.heading < 202.5:
            frame = self.state.nextFrame ("North", pygame.time.get_ticks())
        if self.heading >= 202.5 and self.heading < 247.5:
            frame = self.state.nextFrame ("NorthWest", pygame.time.get_ticks())
        if self.heading >= 247.5 and self.heading < 292.5:
            frame = self.state.nextFrame ("West", pygame.time.get_ticks())
        if self.heading >= 292.5 and self.heading < 337.5:
            frame = self.state.nextFrame ("SouthWest", pygame.time.get_ticks())
        if self.heading >= 337.5 and self.heading <= 360:
            frame = self.state.nextFrame ("South", pygame.time.get_ticks())
        s = frame.image
        self.rRect = s.get_rect()
        return s
    
    def draw (self, screen, grid):
        "draw sprite on screen and animate - to be called from main loop"
        s = self.getDrawSurface()
        # tPosPi = topleft of image
        # so move image up and right to better fit on location
        # FIXME: this has to be a parameter somehow ...!! is that the proper way to deal with it?
        r = s.get_rect()
        pos = vSub (self.tPosPi, (r.w / 2, r.h / 2))
        screen.blit (s, grid.g2s(pos))
        if not DEBUG: return
        pygame.draw.rect(screen, (255,0,0), pygame.Rect(pos, r.size), 1)
        pygame.draw.circle(screen, (255,255,0), self.tPosPi, self.colCircleRad, 1)
        pygame.draw.circle(screen, (255,0,255), self.tPosPi, 3)
        pygame.draw.rect(screen, (255,255,255), pygame.Rect (self.tPosPi, (10,10)), 1)
    def setState(self, statename, activeAnimSetName, *arg):
        self.state = self.dStates[statename]
        self.state.setActiveSet (activeAnimSetName)
        self.state.callback(arg)
    def getState(self):
        # return name of state
        l = self.dStates.items()
        h = [j[1] for j in l]
        if self.state in h:
            i = h.index(self.state)
        else:
            return None
        return l[i][0]
    def hasState (self, statename):
        try:
            self.dStates[statename]
        except KeyError:
            return False
        return True
    def __repr__(self):
        return "<cHuman at:" + str(self.tPosPi) + "/"+str(self._depth)+">"
    def setNewPathToTarget (self, grid, targetobj, blockingTileList = ["blocking"]):
        """calculate new path to targetobj and sets it as current path"""
        #print blockingTileList
        if isinstance(targetobj,blockengine.cSprite):
            dest = grid.getTileAtPi(targetobj.tPosPi)
        else:
            dest = targetobj     
        path = blockengine.findpath (grid, grid.getTileAtPi(self.tPosPi), dest, ["blocking"], blockingTileList)
        if path != []:
            self.setPath (path)
            t = self.getPath().next()
            self.heading = grid.getHeadingToTarget(self.tPosPi, t)
            self.rotate()
    def followPath (self, grid):
        " Move if we are following a path "        
        if self.reachedDestination(grid): return None
        pos = grid.getTileAtPi (self.tPosPi)
        if pos == self.getPath().current:
            t = self.getPath().next()
            self.heading = grid.getHeadingToTarget(self.tPosPi, t)
            self.rotate()  
        return self.simulateMoveForward(self.speed)
    def reachedDestination(self, grid):
        """return true if we reached the destination of the current path"""
        pos = grid.getTileAtPi (self.tPosPi)
        if pos == self.getPath().destination:
            return True
        return False
   

